home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / machserver / 1.098 / libc / vfscanf.c < prev   
C/C++ Source or Header  |  1990-09-11  |  11KB  |  464 lines

  1. /* 
  2.  * vfscanf.c --
  3.  *
  4.  *    Source code for the "vfscanf" library procedure.
  5.  *
  6.  * Copyright 1988 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /sprite/src/lib/c/stdio/RCS/vfscanf.c,v 1.5 90/09/11 14:27:16 kupfer Exp $ SPRITE (Berkeley)";
  18. #endif not lint
  19.  
  20. #include <sprite.h>
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <stdlib.h>
  24. #include <varargs.h>
  25.  
  26. #ifndef TRUE
  27. #define TRUE 1
  28. #endif
  29. #ifndef FALSE
  30. #define FALSE 0
  31. #endif
  32.  
  33. /*
  34.  * Maximum number of bytes in allowable ASCII representation of a
  35.  * floating-point number:
  36.  */
  37.  
  38. #define MAX_FLOAT_SIZE 350
  39. /*
  40.  *----------------------------------------------------------------------
  41.  *
  42.  * vfscanf --
  43.  *
  44.  *    This utility routine does all of the real work of scanning
  45.  *    fields under control of a format string.  It is called by
  46.  *    scanf, fscanf, and sscanf.
  47.  *
  48.  * Results:
  49.  *    Values addressed by elements of args are modified to hold
  50.  *    values scanned from stream.  The return value is a count of
  51.  *    the number of fields successfully scanned from stream, or
  52.  *    EOF if no characters could be input from stream.
  53.  *
  54.  * Side effects:
  55.  *    Information is input from stream.
  56.  *
  57.  *----------------------------------------------------------------------
  58.  */
  59.  
  60. int
  61. vfscanf(stream, format, args)
  62.     register FILE *stream;    /* Where to read characters for parsing. */
  63.     register char *format;    /* Contains literal text and format control
  64.                  * sequences indicating how args are to be
  65.                  * scanned.  See the man page for details. */
  66.     va_list args;        /* Addresses of a variable number of arguments
  67.                  * to be modified. */
  68. {
  69.     int suppress;        /* TRUE means scan value but don't actual
  70.                  * modify an element of args. */
  71.     int storeShort;        /* TRUE means store a short value. */
  72.     int storeLong;        /* TRUE means store a long value. */
  73.     int width;            /* Field width. */
  74.     register char formatChar;     /* Current character from format string.
  75.                  * Eventually it ends up holding the format
  76.                  * type (e.g. 'd' for decimal). */
  77.     register int streamChar;    /* Next character from stream. */
  78.     int assignedFields;        /* Counts number of successfully-assigned
  79.                  * fields. */
  80.     int base;            /* Gives base for numbers:  0 means float,
  81.                  * -1 means not a number.
  82.                  */
  83.     int sign;            /* TRUE means negative sign. */
  84.     char buf[MAX_FLOAT_SIZE+1];
  85.                 /* Place to accumulate floating-point
  86.                  * number for processing. */
  87.     register char *ptr = (char *) NIL;
  88.     char *savedPtr, *end;
  89.  
  90.     assignedFields = 0;
  91.     streamChar = getc(stream);
  92.     if (streamChar == EOF) {
  93.     return(EOF);
  94.     }
  95.  
  96.     /*
  97.      * The main loop is to scan through the characters in format.
  98.      * Anything but a '%' must match the next character from stream.
  99.      * A '%' signals the start of a format field;  the formatting
  100.      * information is parsed, the next value is scanned from the stream
  101.      * and placed in memory, and the loop goes on.
  102.      */
  103.  
  104.     for (formatChar = *format; (formatChar != 0) && (streamChar != EOF);
  105.         format++, formatChar = *format) {
  106.     
  107.     /*
  108.      * A white-space format character matches any number of
  109.      * white-space characters from the stream.
  110.      */
  111.  
  112.     if (isspace(formatChar)) {
  113.         while (isspace(streamChar)) {
  114.         streamChar = getc(stream);
  115.         }
  116.         continue;
  117.     }
  118.  
  119.     /*
  120.      * Any character but % must be matched exactly by the stream.
  121.      */
  122.  
  123.     if (formatChar != '%') {
  124.         if (streamChar != formatChar) {
  125.         break;
  126.         }
  127.         streamChar = getc(stream);
  128.         continue;
  129.     }
  130.  
  131.     /*
  132.      * Parse off the format control fields.
  133.      */
  134.  
  135.     suppress = FALSE;
  136.     storeLong = FALSE;
  137.     storeShort = FALSE;
  138.     width = -1;
  139.     format++;  
  140.     formatChar = *format;
  141.     if (formatChar == '*') {
  142.         suppress = TRUE;
  143.         format++; 
  144.         formatChar = *format;
  145.     }
  146.     if (isdigit(formatChar)) {
  147.         width = strtoul(format, &end, 10);
  148.         format = end;
  149.         formatChar = *format;
  150.     }
  151.     if (formatChar == 'l') {
  152.         storeLong = TRUE;
  153.         format++; 
  154.         formatChar = *format;
  155.     }
  156.     if (formatChar == 'h') {
  157.         storeShort = TRUE;
  158.         format++; 
  159.         formatChar = *format;
  160.     }
  161.  
  162.     /*
  163.      * Skip any leading blanks in the input (except for 'c' format).
  164.      * Also, default the width to infinity, except for 'c' format.
  165.      */
  166.     
  167.     if ((formatChar != 'c') && (formatChar != '[')) {
  168.         while (isspace(streamChar)) {
  169.         streamChar = getc(stream);
  170.         }
  171.     }
  172.     if ((width <= 0) && (formatChar != 'c')) {
  173.         width = 1000000;
  174.     }
  175.  
  176.     /*
  177.      * Check for EOF again after parsing away the white space.
  178.      */
  179.     if (streamChar == EOF) {
  180.         break;
  181.     }
  182.     
  183.     /*
  184.      * Process the conversion character.  For numbers, this just means
  185.      * turning it into a "base" number that indicates how to read in
  186.      * a number.
  187.      */
  188.     
  189.     base = -1;
  190.     switch (formatChar) {
  191.  
  192.         case '%':
  193.         if (streamChar != '%') {
  194.             goto done;
  195.         }
  196.         streamChar = getc(stream);
  197.         break;
  198.         
  199.         case 'D':
  200.         storeShort = FALSE;
  201.         case 'd':
  202.         base = 10;
  203.         break;
  204.         
  205.         case 'O':
  206.         storeShort = FALSE;
  207.         case 'o':
  208.         base = 8;
  209.         break;
  210.         
  211.         case 'X':
  212.         storeShort = FALSE;
  213.         case 'x':
  214.         base = 16;
  215.         break;
  216.         
  217.         case 'E':
  218.         case 'F':
  219.         storeLong = TRUE;
  220.         case 'e':
  221.         case 'f':
  222.         base = 0;
  223.         break;
  224.  
  225.         /*
  226.          * Characters and strings are handled in exactly the same way,
  227.          * except that for characters the default width is 1 and spaces
  228.          * are not considered terminators.
  229.          */
  230.  
  231.         case 'c':
  232.         if (width <= 0) {
  233.             width = 1;
  234.         }
  235.         case 's':
  236.         if (suppress) {
  237.             while ((width > 0) && (streamChar != EOF)) {
  238.             if (isspace(streamChar) && (formatChar == 's')) {
  239.                 break;
  240.             }
  241.             streamChar = getc(stream);
  242.             width--;
  243.             }
  244.         } else {
  245.             ptr = va_arg(args, char *);
  246.             while ((width > 0) && (streamChar != EOF)) {
  247.             if (isspace(streamChar) && (formatChar == 's')) {
  248.                 break;
  249.             }
  250.             *ptr = streamChar;
  251.             ptr++;
  252.             streamChar = getc(stream);
  253.             width--;
  254.             }
  255.             if (formatChar == 's') {
  256.             *ptr = 0;
  257.             }
  258.             assignedFields++;
  259.         }
  260.         break;
  261.         
  262.         case '[':
  263.         format++; formatChar = *format;
  264.         if (formatChar == '^') {
  265.             format++;
  266.         }
  267.         if (!suppress) {
  268.             ptr = va_arg(args, char *);
  269.         }
  270.         savedPtr = format;
  271.         while ((width > 0) && (streamChar != EOF)) {
  272.             format = savedPtr;
  273.             while (TRUE) {
  274.             if (*format == streamChar) {
  275.                 if (formatChar == '^') {
  276.                 goto stringEnd;
  277.                 } else {
  278.                 break;
  279.                 }
  280.             }
  281.             if ((*format == ']') || (*format == 0)) {
  282.                 if (formatChar == '^') {
  283.                 break;
  284.                 } else {
  285.                 goto stringEnd;
  286.                 }
  287.             }
  288.             format++;
  289.             }
  290.             if (!suppress) {
  291.             *ptr = streamChar;
  292.             ptr++;
  293.             }
  294.             streamChar = getc(stream);
  295.             width--;
  296.         }
  297.         stringEnd:
  298.         while ((*format != ']') && (*format != 0)) {
  299.             format++;
  300.         }
  301.         formatChar = *format;
  302.         if (!suppress) {
  303.             *ptr = 0;
  304.             assignedFields++;
  305.         }
  306.         break;
  307.  
  308.         /*
  309.          * Don't ask why, but for compatibility with UNIX, a null
  310.          * conversion character must always return EOF, and any
  311.          * other conversion character must be treated as decimal.
  312.          */
  313.         
  314.         case 0:
  315.         ungetc(streamChar, stream);
  316.         return(EOF);
  317.         
  318.         default:
  319.         base = 10;
  320.         break;
  321.     }
  322.  
  323.     /*
  324.      * If the field wasn't a number, then everything was handled
  325.      * in the switch statement above.  Otherwise, we still have
  326.      * to read in a number.  This gets handled differently for
  327.      * integers and floating-point numbers.
  328.      */
  329.     
  330.     if (base < 0) {
  331.         continue;
  332.     }
  333.  
  334.     if (streamChar == '-') {
  335.         sign = TRUE;
  336.         width -= 1;
  337.         streamChar = getc(stream);
  338.     } else {
  339.         sign = FALSE;
  340.         if (streamChar == '+') {
  341.         width -= 1;
  342.         streamChar = getc(stream);
  343.         }
  344.     }
  345.  
  346.     /*
  347.      * If we're supposed to be parsing a floating-point number, read
  348.      * the digits into a temporary buffer and use the conversion library
  349.      * routine to convert them.
  350.      */
  351.     
  352. #define COPYCHAR \
  353.     *ptr = streamChar; ptr++; width--; streamChar = getc(stream);
  354.  
  355.     if (base == 0) {
  356.         if (width > MAX_FLOAT_SIZE) {
  357.         width = MAX_FLOAT_SIZE;
  358.         }
  359.         ptr = buf;
  360.         while ((width > 0) && isdigit(streamChar)) {
  361.         COPYCHAR;
  362.         }
  363.         if ((width > 0) && (streamChar == '.')) {
  364.         COPYCHAR;
  365.         }
  366.         while ((width > 0) && isdigit(streamChar)) {
  367.         COPYCHAR;
  368.         }
  369.         if ((width > 0) && ((streamChar == 'e') || (streamChar == 'E'))) {
  370.         COPYCHAR;
  371.         if ((width > 0) &&
  372.             ((streamChar == '+') || (streamChar == '-'))) {
  373.             COPYCHAR;
  374.         }
  375.         while ((width > 0) && isdigit(streamChar)) {
  376.             COPYCHAR;
  377.         }
  378.         }
  379.         *ptr = 0;
  380.  
  381.         if (ptr == buf) {        /* Not a valid number. */
  382.         goto done;
  383.         }
  384.  
  385.         if (!suppress) {
  386.         double d;
  387.         d = atof(buf);
  388.         if (sign) {
  389.             d = -d;
  390.         }
  391.         if (storeLong) {
  392.             *(va_arg(args, double *)) = d;
  393.         } else {
  394.             *(va_arg(args, float *)) = d;
  395.         }
  396.         assignedFields++;
  397.         }
  398.     } else {
  399.         /*
  400.          * This is an integer.  Use special-purpose code for the
  401.          * three supported bases in order to make it run fast.
  402.          */
  403.  
  404.         int i;
  405.         int anyDigits;
  406.  
  407.         i = 0;
  408.         anyDigits = FALSE;
  409.         if (base == 10) {
  410.         while ((width > 0) && isdigit(streamChar)) {
  411.             i = (i * 10) + (streamChar - '0');
  412.             streamChar = getc(stream);
  413.             anyDigits = TRUE;
  414.             width -= 1;
  415.         }
  416.         } else if (base == 8) {
  417.         while ((width > 0) && (streamChar >= '0')
  418.             && (streamChar <= '7')) {
  419.             i = (i << 3) + (streamChar - '0');
  420.             streamChar = getc(stream);
  421.             anyDigits = TRUE;
  422.             width -= 1;
  423.         }
  424.         } else {
  425.         while (width > 0) {
  426.             if (isdigit(streamChar)) {
  427.             i = (i << 4) + (streamChar - '0');
  428.             } else if ((streamChar >= 'a') && (streamChar <= 'f')) {
  429.             i = (i << 4) + (streamChar + 10 - 'a');
  430.             } else if ((streamChar >= 'A') && (streamChar <= 'F')) {
  431.             i = (i << 4) + (streamChar + 10 - 'A');
  432.             } else {
  433.             break;
  434.             }
  435.             streamChar = getc(stream);
  436.             anyDigits = TRUE;
  437.             width--;
  438.         }
  439.         }
  440.         if (!anyDigits) {
  441.         goto done;
  442.         }
  443.         if (sign) {
  444.         i = -i;
  445.         }
  446.         if (!suppress) {
  447.         if (storeShort) {
  448.             *(va_arg(args, short *)) = i;
  449.         } else {
  450.             *(va_arg(args, int *)) = i;
  451.         }
  452.         assignedFields++;
  453.         }
  454.     }
  455.     }
  456.  
  457.     done:
  458.     ungetc(streamChar, stream);
  459.     if ((streamChar == EOF) && (assignedFields == 0)) {
  460.     return(EOF);
  461.     }
  462.     return(assignedFields);
  463. }
  464.